using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;

namespace Woz
{
    /// <summary>
    /// defines a status progress bar dialog form that shows the percentage of the status
    /// </summary>
    public class StatusProgressForm : Form
    {
        /// <summary>
        /// initializes a new instance of the status progress bar dialog form
        /// </summary>
        public StatusProgressForm()
        {
            // size the form and hide the close button
            base.FormBorderStyle = FormBorderStyle.FixedToolWindow;
            base.StartPosition = FormStartPosition.CenterScreen;
            base.Text = "";
            base.ClientSize = new Size(292, 64);
            base.ControlBox = false;
            this.TopLevel = true;

            // create the progress bar
            this.mProgressBar = new ProgressBar();
            this.mProgressBar.Location = new Point(12, 12);
            this.mProgressBar.Size = new Size(268, 20);
            this.mProgressBar.Value = 0;
            this.Controls.Add(this.mProgressBar);
            
            // create the status text label
            this.mStatusText = new Label();
            this.mStatusText.Location = new Point(12, 35);
            this.mStatusText.Size = new Size(268, 20);
            this.mStatusText.TextAlign = ContentAlignment.MiddleCenter;
            this.mStatusText.Text = "";
            this.Controls.Add(this.mStatusText);

            // register callbacks
            this.RegisterCallbacks();
        }

        /// <summary>
        /// handles what happens when the forms visibility has changed
        /// </summary>
        /// <param name="pEventArgs">the associated event args</param>
        protected override void OnVisibleChanged(EventArgs pEventArgs)
        {
            this.Owner.Enabled = !this.Visible;
            base.OnVisibleChanged(pEventArgs);
        }

        /// <summary>
        /// disables the closing of the progress form
        /// </summary>
        /// <param name="pCancelEventArgs">the associated cancel event args</param>
        protected override void OnClosing(CancelEventArgs pCancelEventArgs)
        {
            pCancelEventArgs.Cancel = true;
            base.OnClosing(pCancelEventArgs);
        }
        
        /// <summary>
        /// registers all the callbacks
        /// </summary>
        private void RegisterCallbacks()
        {
            // set the set status text method of the resource manager
            Woz.ResourceManager.Instance.SetStatusTextMethod = new Woz.ResourceManager.SetStatusTextHandler(this.cSetStatusText);

            // set the progress start method of the resource manager
            Woz.ResourceManager.Instance.ProgressStartMethod = new Woz.ResourceManager.ProgressStartHandler(this.cProgressStart);

            // set the progress update method of the resource manager
            Woz.ResourceManager.Instance.ProgressUpdateMethod = new Woz.ResourceManager.ProgressUpdateHandler(this.cProgressUpdate);

            // set the progress end method of the resource manager
            Woz.ResourceManager.Instance.ProgressEndMethod = new Woz.ResourceManager.ProgressEndHandler(this.cProgressEnd);
        }

        /// <summary>
        /// sets the status text to the given text for the given duration in seconds
        /// </summary>
        /// <param name="pText">the text to display on the status</param>
        /// <param name="pDuration">the number of seconds to show the text for</param>
        private void cSetStatusText(string pText, int pDuration)
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new Woz.ResourceManager.SetStatusTextHandler(this.cSetStatusText),
                    new object[] { pText, pDuration });
            }
            else
            {
                // set the status text
                this.mStatusText.Text = pText;

                // start the timer
                this.mStatusTimer = new System.Threading.Timer(this.cUnsetStatusText);
                this.mStatusTimer.Change(pDuration * 1000, System.Threading.Timeout.Infinite);

                // refresh the thread so we see our changes
                this.Refresh();
            }
        }

        /// <summary>
        /// clears the status text immidiately
        /// </summary>
        /// <param name="pSender">the calling object of the function</param>
        private void cUnsetStatusText(object pSender)
        {
            // stop the timer
            if (this.mStatusTimer != null)
            {
                this.mStatusTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
                this.mStatusTimer = null;
            }

            // clear the status text
            if (this.InvokeRequired)
            {
                this.Invoke(new System.Threading.TimerCallback(this.cUnsetStatusText),
                    new object[] { pSender });
            }
            else
            {
                // clear the text
                this.mStatusText.Text = "";

                // hide this form
                this.Hide();

                // invoke the method
                if (this.mMethod != null)
                {
                    // WARNING: make sure to set the this.mmethod to null BEFORE invoking the method
                    EventHandler method = this.mMethod;
                    this.mMethod = null;
                    method.Invoke(this, EventArgs.Empty);
                }
            }
        }

        /// <summary>
        /// starts the progress form and initializes the title and status text to the given progress text
        /// </summary>
        /// <param name="pProgressText">the title and status of the progress form</param>
        /// <param name="pMethod">the method to execute after progress is done</param>
        private void cProgressStart(string pProgressText, EventHandler pMethod)
        {
            // clear the timer if any
            this.cUnsetStatusText(null);

            // make sure we invoke on the current thread if invoke is required
            if (this.InvokeRequired)
            {
                this.Invoke(new Woz.ResourceManager.ProgressStartHandler(this.cProgressStart),
                    new object[] { pProgressText, pMethod });
            }
            else
            {
                // save the method
                this.mMethod = pMethod;

                // set the title of the progress form
                this.Text = pProgressText;

                // restart the progress bar
                this.mProgressBar.Value = 0;

                // set the label text
                this.mStatusText.Text = String.Format("{0} : 0%", pProgressText);

                // save the progress text
                this.mProgressText = pProgressText;

                // display it now
                this.Show(Woz.MainForm.Instance);
            }
        }

        /// <summary>
        /// updates the progress percentages
        /// </summary>
        /// <param name="pPercent">the percent to set it to</param>
        private void cProgressUpdate(int pPercent)
        {
            // make sure we invoke on the current thread if invoke is required
            if (this.InvokeRequired)
            {
                // invoke this call into the thread that created this form
                this.Invoke(new Woz.ResourceManager.ProgressUpdateHandler(this.cProgressUpdate),
                    new object[] { pPercent });
            }
            else
            {
                // set the progress value
                this.mProgressBar.Value = pPercent;

                // set the status text
                this.mStatusText.Text = String.Format("{0} : {1}%", this.mProgressText, pPercent);

                // refresh the thread so we see our changes
                this.Refresh();
            }
        }

        /// <summary>
        /// ends the progress dialog by setting the progress to a hundred percent and then closing itself
        /// </summary>
        /// <param name="pDuration">the amount of time to wait before the status dissapears</param>
        private void cProgressEnd(int pDuration)
        {
            // make sure we invoke on the current thread if invoke is required
            if (this.InvokeRequired)
            {
                // invoke this call into the thread that created this form
                this.Invoke(new Woz.ResourceManager.ProgressEndHandler(this.cProgressEnd));
            }
            else
            {
                // set the progress bar to one hundred
                this.mProgressBar.Value = 100;

                // call set status text to show the progress text for three seconds
                this.cSetStatusText(String.Format("{0} - 100%", this.mProgressText), pDuration);

                // clear the progress text
                this.mProgressText = "";
            }
        }

        /// <summary>
        /// what to do next after the progress bar goes away
        /// </summary>
        private EventHandler mMethod;

        /// <summary>
        /// the progress bar to show how far weve come
        /// </summary>
        private ProgressBar mProgressBar;

        /// <summary>
        /// the label to display the progress text
        /// </summary>
        private Label mStatusText;

        /// <summary>
        /// the text to use for the progress bar
        /// </summary>
        private string mProgressText;

        /// <summary>
        /// the timer to use to control how long status text should appear
        /// </summary>
        private System.Threading.Timer mStatusTimer;
    }
}
